﻿
// 2020/8/26: 首个版本（根据EnvSampleV2.cs 2020/1/14版本修改）
// 2020/11/3: 新增气压、相对湿度、能见度、风向、风速、PM10、道路状况、车道交通状况、灾害情况等字段。道路交通状况更名

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ASEva;

namespace EnvSampleV3
{
    enum WeatherType
    {
        Unknown = 0, // 未知
        Sunny = 1, // 晴
        Cloudy = 2, // 阴
        Rainy = 3, // 雨
        Snowy = 4, // 雪
        Foggy = 5, // 雾
        Sand = 6, // 沙尘
    };

    enum RoadType
    {
        Unknown = 0, // 未知
        Highway = 1, // 高速路，无红绿灯，80~120KPH
        CityExpress = 2, // 城市快速路，无红绿灯，60~80KPH
        CityMain = 3, // 城市主干道，有红绿灯，推荐60KPH
        GeneralRoad = 4, // 一般道路，有红绿灯，推荐40KPH
        CountryRoad = 5, // 乡村道路，无红绿灯，~40KPH
    };

    enum SpotType
    {
        Unknown = 0,  //未知
        TollGate = 1, // 收费站
        ServiceArea = 2, // 服务区
        Tunnel = 3, // 隧道
        Accident = 4, // 事故
        Crossing = 5, // 路口
        Roundabout = 7, // 环岛
        HighwayEntrance = 8, // 高速入口
        HighwayExit = 9, // 高速出口
        Bridge = 10, // 桥
        ExpressEntrance = 11, // 快速路入口
        ExpressExit = 12, // 快速路出口
    }

    class Spot
    {
        public SpotType Type { get; set; }  // 场所类型
        public double Distance { get; set; } // 到场所的距离 m
    }

    enum TrafficStatus
    {
        Unknown = 0, // 未知
        Open = 1, // 畅通
        Slow = 2, // 缓慢
        Crowded = 3, // 拥挤
        Reverse = 4, // 逆向（仅限相邻车道交通状况）
    };

    enum WindDirection
    {
        Unknown = 0, // 未知
        East = 1, // 东风
        South = 2, // 南风
        West = 3, // 西风
        North = 4, // 北风
        SouthEast = 5, // 东南风
        NorthEast = 6, // 东北风
        SouthWest = 7, // 西南风
        NorthWest = 8, // 西北风
    };

    enum RoadSurfaceStatus
    {
        Unknown = 0, // 未知
        Dry = 1, // 干燥
        Wet = 2, // 湿滑
        SnowCovered = 3, // 积雪
        Frozen = 4, // 结冰
    };

    enum DisasterStatus
    {
        Unknown = 0, // 未知
        No = 1, // 无灾害
        Yes = 2, // 有灾害
    };

    class EnvSample : Sample
    {
        public WeatherType WeatherType { get; set; }   //天气类型
        public double? Temperature { get; set; } // 温度 °C
        public double? Humidity { get; set; } // 相对湿度 %
        public double? Pressure { get; set; } // 气压 mb
        public double? PM25 { get; set; } // PM 2.5 ug/m3
        public double? PM10 { get; set; } // ug/m3
        public double? Visibility { get; set; } // 能见度 KM
        public double? Illumination { get; set; } // 光照强度 lux
        public double? WindSpeed { get; set; }  // 风速 m/s
        public WindDirection WindDirection { get; set; } // 风向
        public DisasterStatus TyphoonStatus { get; set; } // 台风状况
        public DisasterStatus TornadoStatus { get; set; } // 龙卷风状况
        public DisasterStatus LightningStatus { get; set; } // 雷电状况
        public DisasterStatus HailStatus { get; set; } // 冰雹状况
        public String RoadName { get; set; } // 道路名称
        public int? SpeedLimit { get; set; } // 道路限速 KPH
        public RoadType RoadType { get; set; } // 道路类型
        public RoadSurfaceStatus RoadSurfaceStatus { get; set; } // 路面状况
        public TrafficStatus RoadTraffic { get; set; } // 道路交通状况
        public TrafficStatus CurrentLaneTraffic { get; set; } // 本车道交通状况
        public TrafficStatus LeftLaneTraffic { get; set; } // 左车道交通状况
        public TrafficStatus RightLaneTraffic { get; set; } // 右车道交通状况
        public List<Spot> Spots { get; set; } // 场所列表

        public EnvSample()
        {
            Spots = new List<Spot>();
        }

        public static string Title
        {
            get
            {
                return "Road name size,Spot count,Weather,Temperature[°C],PM2.5[ug/m3],Speed limit[KPH],Road type,Illumination[lux],Wind speed[m/s],Road traffic status,Road name,Pressure[mb],Humidity[%],Visibility[KM],Wind direction,PM10[ug/m3],Road surface status,Current lane traffic status,Left lane traffic status,Right lane traffic status,Typhoon status,Tornado status,Lightning status,Hail status,...";
            }
        }

        public static string Protocol
        {
            get
            {
                return "env-sample-v3";
            }
        }

        public static string[] Protocols
        {
            get
            {
                return new string[] { "env-sample-v1", "env-sample-v2", "env-sample-v3" };
            }
        }

        public override string GetGeneralSampleProtocol()
        {
            return Protocol;
        }

        public override string[] GetGeneralSampleProtocols()
        {
            return Protocols;
        }

        public override bool FromGeneralSample(GeneralSample sample)
        {
            if (sample.Protocol == "env-sample-v1")
            {
                if (sample.Values.Count < 1) return false;

                int roadNameSize = (int)sample.Values[0].number;
                if (sample.Values.Count != 13 + roadNameSize) return false;

                WeatherType = sample.Values[1].IsNumber() ? (WeatherType)(int)sample.Values[1].number : WeatherType.Unknown;
                Temperature = sample.Values[2].ToDouble();
                PM25 = sample.Values[3].ToDouble();
                SpeedLimit = sample.Values[4].IsNumber() ? (int?)(int)sample.Values[4].number : null;
                RoadType = sample.Values[5].IsNumber() ? (RoadType)(int)sample.Values[5].number : RoadType.Unknown;
                Illumination = sample.Values[6].ToDouble();
                WindSpeed = sample.Values[7].ToDouble();
                RoadTraffic = sample.Values[12].IsNumber() ? (TrafficStatus)(int)sample.Values[12].number : TrafficStatus.Unknown;

                var roadNameBin = new byte[roadNameSize];
                for (int i = 0; i < roadNameSize; i++)
                {
                    roadNameBin[i] = (byte)sample.Values[13 + i].ToInt().Value;
                }
                RoadName = Encoding.UTF8.GetString(roadNameBin);

                Base = sample.Base;
                Offset = sample.Offset;
                Timeline = sample.Timeline;

                return true;
            }
            else if (sample.Protocol == "env-sample-v2")
            {
                if (sample.Values.Count < 2) return false;

                int roadNameSize = (int)sample.Values[0].number;
                int spotCount = (int)sample.Values[1].number;

                if (sample.Values.Count != 10 + 2 * spotCount + roadNameSize) return false;

                WeatherType = sample.Values[2].IsNumber() ? (WeatherType)(int)sample.Values[2].number : WeatherType.Unknown;
                Temperature = sample.Values[3].ToDouble();
                PM25 = sample.Values[4].ToDouble();
                SpeedLimit = sample.Values[5].IsNumber() ? (int?)(int)sample.Values[5].number : null;
                RoadType = sample.Values[6].IsNumber() ? (RoadType)(int)sample.Values[6].number : RoadType.Unknown;
                Illumination = sample.Values[7].ToDouble();
                WindSpeed = sample.Values[8].ToDouble();
                RoadTraffic = sample.Values[9].IsNumber() ? (TrafficStatus)(int)sample.Values[9].number : TrafficStatus.Unknown;

                Spots = new List<Spot>();
                for (int i = 0; i < spotCount; i++)
                {
                    var spotType = sample.Values[10 + 2 * i].ToInt();
                    var spotDistance = sample.Values[10 + 2 * i + 1].ToDouble();
                    if (spotType != null && spotDistance != null)
                    {
                        Spots.Add(new Spot()
                            {
                                Type = (SpotType)spotType.Value,
                                Distance = spotDistance.Value,
                            });
                    }
                }

                var roadNameBin = new byte[roadNameSize];
                for (int i = 0; i < roadNameSize; i++)
                {
                    roadNameBin[i] = (byte)sample.Values[10 + 2 * spotCount + i].ToInt().Value;
                }
                RoadName = Encoding.UTF8.GetString(roadNameBin);

                Base = sample.Base;
                Offset = sample.Offset;
                Timeline = sample.Timeline;

                return true;
            }
            else if (sample.Protocol == "env-sample-v3")
            {
                if (sample.Values.Count < 2) return false;

                int roadNameSize = (int)sample.Values[0].number;
                int spotCount = (int)sample.Values[1].number;

                if (sample.Values.Count != 64 + 2 * spotCount + roadNameSize) return false;

                WeatherType = sample.Values[2].IsNumber() ? (WeatherType)(int)sample.Values[2].number : WeatherType.Unknown;
                Temperature = sample.Values[3].ToDouble();
                PM25 = sample.Values[4].ToDouble();
                SpeedLimit = sample.Values[5].IsNumber() ? (int?)(int)sample.Values[5].number : null;
                RoadType = sample.Values[6].IsNumber() ? (RoadType)(int)sample.Values[6].number : RoadType.Unknown;
                Illumination = sample.Values[7].ToDouble();
                WindSpeed = sample.Values[8].ToDouble();
                RoadTraffic = sample.Values[9].IsNumber() ? (TrafficStatus)(int)sample.Values[9].number : TrafficStatus.Unknown;

                Pressure = sample.Values[11].ToDouble();
                Humidity = sample.Values[12].ToDouble();
                Visibility = sample.Values[13].ToDouble();
                WindDirection = sample.Values[14].IsNumber() ? (WindDirection)sample.Values[14].number : WindDirection.Unknown;
                PM10 = sample.Values[15].ToDouble();
                RoadSurfaceStatus = sample.Values[16].IsNumber() ? (RoadSurfaceStatus)sample.Values[16].number : RoadSurfaceStatus.Unknown;
                CurrentLaneTraffic = sample.Values[17].IsNumber() ? (TrafficStatus)sample.Values[17].number : TrafficStatus.Unknown;
                LeftLaneTraffic = sample.Values[18].IsNumber() ? (TrafficStatus)sample.Values[18].number : TrafficStatus.Unknown;
                RightLaneTraffic = sample.Values[19].IsNumber() ? (TrafficStatus)sample.Values[19].number : TrafficStatus.Unknown;
                TyphoonStatus = sample.Values[20].IsNumber() ? (DisasterStatus)sample.Values[20].number : DisasterStatus.Unknown;
                TornadoStatus = sample.Values[21].IsNumber() ? (DisasterStatus)sample.Values[21].number : DisasterStatus.Unknown;
                LightningStatus = sample.Values[22].IsNumber() ? (DisasterStatus)sample.Values[22].number : DisasterStatus.Unknown;
                HailStatus = sample.Values[23].IsNumber() ? (DisasterStatus)sample.Values[23].number : DisasterStatus.Unknown;

                Spots = new List<Spot>();
                for (int i = 0; i < spotCount; i++)
                {
                    var spotType = sample.Values[64 + 2 * i].ToInt();
                    var spotDistance = sample.Values[64 + 2 * i + 1].ToDouble();
                    if (spotType != null && spotDistance != null)
                    {
                        Spots.Add(new Spot()
                        {
                            Type = (SpotType)spotType.Value,
                            Distance = spotDistance.Value,
                        });
                    }
                }

                if (sample.Values[10].mode == GeneralSampleValueMode.Text)
                {
                    RoadName = sample.Values[10].text;
                }
                else
                {
                    var roadNameBin = new byte[roadNameSize];
                    for (int i = 0; i < roadNameSize; i++)
                    {
                        roadNameBin[i] = (byte)sample.Values[64 + 2 * spotCount + i].ToInt().Value;
                    }
                    RoadName = Encoding.UTF8.GetString(roadNameBin);
                }

                Base = sample.Base;
                Offset = sample.Offset;
                Timeline = sample.Timeline;

                return true;
            }

            return false;
        }
    }
}
